/**
 * MINAPL Compiler.
 * Copyright 2012 Michael A. Muller
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

#include <assert.h>
#include <stdlib.h>
#include <malloc.h>
#include <stdint.h>
#include <unistd.h>
#include <errno.h>
#include <memory.h>
#include <sys/mman.h>

#include "Array.h"
#include "Buffer.h"
#include "CodeGen.h"
#include "Token.h"
#include "Tokenizer.h"

using namespace minapl;

// trace memory management
bool traceMem = false;

struct Parser;

struct Type {
    Parser *parser;
    Type(Parser *parser) : parser(parser) {}
    ~Type();
};

struct ASTNode {
    Type *type;
    ASTNode(Type *type) : type(type) {}
    virtual ~ASTNode() {}
    virtual void emit(CodeGen *cg) = 0;
};

struct CompileContext;

struct Parser {
    void *manager;
    Parser() : manager(0) {}

    virtual ASTNode *parse(CompileContext *context) = 0;

    /**
     * The isolate function returns a parser that can stand in for this parser
     * that doesn't refer to any external data-structures (specifically AST
     * nodes - it can refer to other things that are either globally scoped or
     * managed by the context or a parent context).
     *
     * Derived classes should override this if necessary.
     */
    virtual Parser *isolate() { return this; }

    virtual ~Parser() {}
};

Type::~Type() {
    delete parser;
}

struct Entry {
    const char *name;
    Parser *parser;
};

/**
 * A symbol table.  Maps character strings to Parser objects.  Does not assume
 * ownership of either the string or the parser.
 */
struct SymTab {
    // symtab - beginning of the Entry array
    // symtabNext - next available slot
    // symtabEnd - end of the allocated table.
    Entry *symtab, *symtabNext, *symtabEnd;

    SymTab() : symtab(0), symtabNext(0), symtabEnd(0) {}

    /// construct the symtab from another one and steal its table.
    SymTab(SymTab &other) :
        symtab(other.symtab),
        symtabNext(other.symtabNext),
        symtabEnd(other.symtabEnd) {

        other.symtab = other.symtabNext = other.symtabEnd = 0;
    }

    ~SymTab() {
        delete [] symtab;
    }

    void add(const char *name, Parser *parser) {
        if (!symtab) {
            symtab = symtabNext = new Entry[1024];
            symtabEnd = symtab + 1024;
        } else if (symtabNext == symtabEnd) {
            size_t curSize = symtabEnd - symtab;
            symtab = new Entry[(symtabEnd - symtab) * 2];
            symtabNext = symtab + curSize;
        }

        symtabNext->name = name;
        symtabNext->parser = parser;
        ++symtabNext;
    }

    Entry *findEntry(const char *name) {
        if (!symtab)
            return 0;

        for (Entry *cur = symtab; cur != symtabNext; ++cur)
            if (!strcmp(cur->name, name))
                return cur;

        return 0;
    }

    Parser *lookUp(const char *text) {
        Entry *entry = findEntry(text);
        if (entry)
            return entry->parser;
        else
            return 0;
    }

    Entry *begin() {
        return symtab;
    }

    Entry *end() {
        return symtabNext;
    }
};

struct ParseError {
    char *message;
    ParseError(char *message) : message(message) {}
    ~ParseError() { delete message; }
};

// A block is a sequence of expressions
struct BlockAST : public ASTNode {
    Array<ASTNode *> nodes;

    BlockAST(Type *type) : ASTNode(type), nodes(4) {}
    ~BlockAST() {
        for (int i = 0; i < nodes.size; ++i)
            delete nodes[i];
    }

    void add(ASTNode *node) { nodes.add(node); }

    void emit(CodeGen *cg) {
        for (int i = 0; i < nodes.size; ++i)
            nodes[i]->emit(cg);
    }
};

BlockAST *parseBlock(CompileContext *context);

/**
 * All of the type objects.  This class has two purposes:
 * 1) it allows the types that must be available to the parsers (for example,
 * 'word' and 'str' to be easily passed around with the compile context.
 * 2) It takes ownership of all created types.  These can't be owned by the
 * root context like most other things because types are referenced by
 * ASTNodes which can live on past the compile context (even though the
 * types aren't currently used from the ASTNodes after parsing)
 *
 * An instance of this structure must live throughout parsing and code
 * generation.
 */
struct Types {
    Type *word, *str;
    Array<Type *> otherTypes;

    Types() : word(0), str(0), otherTypes(16) {}

    ~Types() {
        delete word;
        delete str;

        for (int i = 0; i < otherTypes.size; ++i)
            delete otherTypes[i];
    }

    /**
     * Add a new type to be managed.
     */
    void add(Type *type) {
        otherTypes.add(type);
    }
};

struct ModuleSymTab : public SymTab {
    ~ModuleSymTab() {
        // we actually own the key/values in this case
        for (Entry *e = begin(); e < end(); ++e) {
            free(const_cast<char *>(e->name));
            delete reinterpret_cast<SymTab *>(e->parser);
        }
    }

    SymTab *lookUpModule(const char *name) {
        return reinterpret_cast<SymTab *>(lookUp(name));
    }

    void add(const char *name, SymTab *module) {
        SymTab::add(strdup(name), reinterpret_cast<Parser *>(module));
    }
};

struct CompileContext {

    CompileContext *parent, *root;
    Tokenizer *toker; // toker only needs to be in a non-root context
    Types *types;

    // the offset (actually a negative index, not a byte offset) of the last 
    // local variable.  This value should always be negative (or zero, when 
    // there are no variables).
    int lastLocalVarOffset;

    SymTab symtab;

    // This is used to pass the left-side node (the "primary" or "receiver")
    // into a sub-parser to allow post-parsing.
    ASTNode *primary;

    // these variables should be refactored into a RootCompileContext class.

    // List of parsers to delete when this context is deleted.  These are
    // managed by the root context.
    Array<Parser *> ownedParsers;

    // list of allocated strings
    Array<char *> ownedStrs;

    CodeGen cg;

    // the module cache.  This is a bit of a perversion, since it actually
    // maps a string to a module
    ModuleSymTab modules;

    // root context constructor
    CompileContext(Types *types) :
        parent(0),
        root(0),
        toker(0),
        types(types),
        lastLocalVarOffset(0),
        primary(0),
        ownedParsers(16),
        ownedStrs(16) {
    }

    // child context constructor
    // if 'topLevel' is true, set the last local variable offset to zero, 
    // otherwise uses the offset of the parent.
    CompileContext(CompileContext *parent, bool topLevel) :
        parent(parent),
        root(parent->root ? parent->root : parent),
        toker(parent->toker),
        lastLocalVarOffset(topLevel ? 0 : parent->lastLocalVarOffset),
        types(parent->types),
        primary(0),
        ownedParsers(16),
        ownedStrs(16) {
    }

    ~CompileContext() {
        for (int i = 0; i < ownedParsers.size; ++i) {
            if (traceMem)
                printf("deleting %p at %d of %p\n",
                    ownedParsers[i], i, &ownedParsers);
            delete ownedParsers[i];
        }
        for (int i = 0; i < ownedStrs.size; ++i)
            free(ownedStrs[i]);
    }
    
    CompileContext *getRoot() { return root ? root : this; }

    Parser *lookUp(char *text) {
        CompileContext *ctx = this;
        while (ctx) {
            Parser *result = ctx->symtab.lookUp(text);
            if (result)
                return result;
            else
                ctx = ctx->parent;
        }
        return 0;
    }

    SymTab *lookUpModule(const char *name) {
        if (root)
            return root->modules.lookUpModule(name);
        else
            return modules.lookUpModule(name);
    }

    void storeModule(const char *name, SymTab *module) {
        modules.add(name, module);
    }

    Parser *manage(Parser *parser) {
        if (parser->manager) {
            fprintf(stderr, "parser %p already managed by %p\n", parser, 
                    parser->manager
                    );
            abort();
        }
        if (root) {
            root->ownedParsers.add(parser);
            parser->manager = root;
            if (traceMem)
                printf("adding parser %p to root array at %p, new size is %d\n",
                       parser, &root->ownedParsers, root->ownedParsers.size
                       );
        } else {
            ownedParsers.add(parser);
            parser->manager = this;
            if (traceMem)
                printf("adding parser %p to array at %p, new size is %d\n",
                       parser, &ownedParsers, ownedParsers.size
                       );
        }
        return parser;
    }

    char *manage(char *str) {
        if (root)
            root->ownedStrs.add(str);
        else
            ownedStrs.add(str);
        return str;
    }

    void add(const char *name, Parser *parser) {
        symtab.add(name, parser);
        manage(parser);
    }

    void getToken(Token *tok) {
        toker->getToken(tok);
    }

    void putBack(Token *token) {
        toker->putBack(token);
    }

    int getLineNum() {
        return toker->lineNum;
    }
    
    /**
     * Returns the stack offset between the two constants.
     * Conceptually, this is the number of bytes we need to add to go from 
     * 'other' to this context, or (this->lastLocalVarOffset - 
     * other->lastLocalVarOffset) * sizeof(word)
     */
    int stackOffset(CompileContext *other) {
        return (lastLocalVarOffset - other->lastLocalVarOffset) * 
               sizeof(void *);
    }

    void error(const char *text) {
        static char format[] = "Error:%s:%d: %s";
        size_t size = sizeof(format) + strlen(text) +
                      strlen(toker->sourceName) + 10;
        char *message = (char *)malloc(size);
        snprintf(message, size, format, toker->sourceName, getLineNum(), text);
        throw ParseError(message);
    }

    void error(const char *text, Token *tok) {
        static char format[] = "Error:%s:%d: %s";
        size_t size = sizeof(format) + strlen(text) + tok->repLen() +
            strlen(toker->sourceName) + 10;
        char *message = (char *)malloc(size);
        size_t end = snprintf(message, size, format, toker->sourceName,
                              getLineNum(),
                              text);
        tok->toRep(message + end);
        throw ParseError(message);
    }

    void expect(int tokType, const char *message) {
        Token tok;
        getToken(&tok);
        if (tok.type != tokType)
            error(message, &tok);
    }

    /**
     * Parse and run a module from the current tokenizer.
     */
    ASTNode *parseAndRun() {
        ASTNode *ast = parseBlock(this);

        // store the current code as an offset
        size_t offset = root->cg.curCode - root->cg.codeBuffer;

        // generate the code
        root->cg.emitFuncEnter();
        root->cg.emitSPAdd(lastLocalVarOffset * sizeof(void *));
        ast->emit(&root->cg);
        root->cg.emitSPAdd(-lastLocalVarOffset * sizeof(void *));
        root->cg.emitReturn();

        // convert to executable and execute!
        if (mprotect(root->cg.codeBuffer, root->cg.codeSize,
                    PROT_READ | PROT_WRITE | PROT_EXEC
                    )
            ) {
            fprintf(stderr, "failed to mprotect output buffer: %s\n",
                    strerror(errno)
                    );
            abort();
        }

        void (*func)() = (void (*)()) (root->cg.codeBuffer + offset);
        func();

        return ast;
    }

    /**
     * Load the module, return its top-level AST node.  At the close of this
     * method, the symtab is populated with any commands defined in the module.
     * Note that parsers from the module may reference elements of the AST
     * tree returned - if the context is to be preserved after the AST tree
     * is deleted, you should call isolate() to free it from any such
     * references.
     */
    ASTNode *loadModule(const char *name, FILE *file) {

        // parse the block
        Tokenizer moduleToker(name, file);
        toker = &moduleToker;
        return parseAndRun();
    }

    /**
     * Evaluate the specified string, return its top-level AST node.  This is 
     * equivalent to loadModule() for a string.
     */    
    ASTNode *eval(char *text) {
        FILE *fp = fmemopen(text, strlen(text), "r");
        Tokenizer stringToker("string", fp);
        toker = &stringToker;
        return parseAndRun();
    }

    /**
     * Make the context hermetic - after running this, the parsers in the
     * module symbol table should refer to no external data structures.
     *
     * We use this after loading a module so we can release the module's ast
     * tree but import the module's symbols.
     */
    void isolate() {
        for (Entry *cur = symtab.begin(); cur < symtab.end(); ++cur) {
            Parser *parser = cur->parser->isolate();
            cur->parser = parser;
            if (!parser->manager)
                manage(parser);
        }
    }

    /**
     * Returns the primary and sets it to zero, allowing the caller to consume
     * it.
     */
    ASTNode *releasePrimary() {
        ASTNode *p = primary;
        primary = 0;
        return p;
    }
};

struct ConstAST : public ASTNode {
    void *addr;

    virtual void emit(CodeGen *cg) {
        cg->loadAddr(addr);
    }

    ConstAST(Type *type, void *addr) : ASTNode(type), addr(addr) {}

};

struct ConstParser : public Parser {
    Type *type;
    void *addr;

    ConstParser(Type *type, void *addr) : type(type), addr(addr) {}

    ASTNode *parse(CompileContext *context) {
        return new ConstAST(type, addr);
    }
};

struct NopAST : public ASTNode {
    NopAST(Type *type) : ASTNode(type) {}
    void emit(CodeGen *) {}
};

ASTNode *parseExpression(CompileContext *context) {
    Token tok;
    ASTNode *expr;
    context->getToken(&tok);
    if (tok.type == TOK_ID) {
        Parser *parser = context->lookUp(tok.text);
        if (!parser)
            context->error("Undefined symbol: ", &tok);
        expr = parser->parse(context);
    } else if (tok.type == TOK_STR) {
        expr = new ConstAST(context->types->str, tok.text);
    } else if (tok.type == TOK_INT) {
        expr = new ConstAST(context->types->word, (void *)tok.size);
    } else if (tok.type == TOK_LPAREN) {
        ASTNode *result = parseBlock(context);
        context->expect(TOK_RPAREN,
                        "Expected end parenthesis after parenthesized "
                         "expression, got "
                        );
        expr = result;
    } else if (tok.isTerminator()) {
        context->putBack(&tok);
        return new NopAST(context->types->word);
    } else {
        context->error("Unexpected token: ", &tok);
    }

    // if the expression type has a parser, allow it to parse the remainder of
    // the expression.  Do this in a loop so we can chain these.
    while (expr->type->parser) {
        context->primary = expr;
        ASTNode *result = expr->type->parser->parse(context);
        context->releasePrimary();

        // if we got the same ASTNode back, we're done
        if (result == expr)
            break;
        else
            expr = result;
    }

    return expr;
}

BlockAST *parseBlock(CompileContext *context) {
    BlockAST *block = new BlockAST(0);
    while (true) {
        Token tok;
        context->getToken(&tok);

        // check for semicolon
        if (tok.type == TOK_SEMI) {
            ;  // next iteration

        // check for any other kind of terminator
        } else if (tok.isTerminator()) {
            context->putBack(&tok);
            if (block->nodes.size)
                block->type = block->nodes[block->nodes.size - 1]->type;
            else
                block->type = context->types->word;
            return block;
        } else {
            context->putBack(&tok);
            block->add(parseExpression(context));
        }
    }
}

struct FuncCallAST : public ASTNode {
    void *addr;
    int argCount;
    ASTNode **args;

    FuncCallAST(Type *type, void *addr, int argCount) :
        ASTNode(type),
        addr(addr),
        argCount(argCount),
        args(new ASTNode *[argCount]) {
    }

    ~FuncCallAST() {
        for (int i = 0; i < argCount; ++i)
            delete args[i];
        delete [] args;
    }

    void emitArgs(CodeGen *cg) {
        int i;
        for (i = argCount - 1; i >= 0; --i) {
            args[i]->emit(cg);
            cg->emitStoreArg(i, HINT_CALL);
        }
    }

    void emit(CodeGen *cg) {
        emitArgs(cg);
        cg->emitFuncCall(addr);
        cg->emitSPAdd(argCount * sizeof(void *));
    }

};

struct FuncCallParser : Parser {
    void *addr;
    int argCount;
    Type *returnType;

    void innerParse(CompileContext *context, FuncCallAST *ast) {

        // if there's a primary, use it as the first argument
        int i = 0;
        if (context->primary) {
            ast->args[i] = context->releasePrimary();
            ++i;
        }

        // parse the argument list
        for (; i < argCount; ++i)
            ast->args[i] = parseExpression(context);
    }

    ASTNode *parse(CompileContext *context) {
        FuncCallAST *ast = new FuncCallAST(returnType, addr, argCount);
        innerParse(context, ast);
        return ast;
    }

    FuncCallParser(void *addr, Type *returnType, int argCount) : 
        addr(addr), 
        argCount(argCount),
        returnType(returnType) {
    }
};

// Reference to a local variable.
struct LocalVarRefAST : public ASTNode {
    int index;
    LocalVarRefAST(Type *type, int index) : ASTNode(type), index(index) {}
    void emit(CodeGen *cg) {
        cg->emitLocalVarRef(index);
    }
};

// assignment to a local variable
struct LocalVarStoreAST : public ASTNode {
    int index;
    ASTNode *value;
    LocalVarStoreAST(Type *type, ASTNode *value, int index) :
        ASTNode(type), value(value), index(index) {
    }
    ~LocalVarStoreAST() {
        delete value;
    }
    void emit(CodeGen *cg) {
        value->emit(cg);
        cg->emitLocalVarStore(index);
    }
};

struct VarRefParser : public Parser {
    virtual ASTNode *createStoreAST(ASTNode *value) = 0;
};

struct LocalVarRefParser : public VarRefParser {
    int index;
    Type *type;
    LocalVarRefParser(Type *type, int index) : type(type), index(index) {}
    virtual ASTNode *parse(CompileContext *context) {
        Token tok;
        context->getToken(&tok);

        // if this is the assignment operator, return an assisgnment AST
        if (tok.type == TOK_ID && !strcmp(tok.text, "=")) {
            ASTNode *value = parseExpression(context);
            return new LocalVarStoreAST(type, value, index);
        } else {
            context->putBack(&tok);
            return new LocalVarRefAST(type, index);
        }
    }

    virtual ASTNode *createStoreAST(ASTNode *value) {
        return new LocalVarStoreAST(type, value, index);
    }
};

// definition of an automatic variable
struct AutoVarDefAST : public ASTNode {
    int index;
    ASTNode *initValue;

    AutoVarDefAST(Type *type, int index, ASTNode *initValue) :
        ASTNode(type),
        index(index),
        initValue(initValue) {
    }

    ~AutoVarDefAST() { delete initValue; }

    void emit(CodeGen *cg) {
        if (initValue)
            initValue->emit(cg);
        else
            cg->loadZero();
        cg->emitLocalVarStore(index);
    }
};

struct VarDefParser : public Parser {
    ASTNode *parse(CompileContext *context) {
        Token tok;
        context->getToken(&tok);
        if (tok.type != TOK_ID) {
            context->error("Expected identifier.");
            return 0;
        }

        // get the variable name as a null-terminated string.
        char *varName = context->manage(tok.copyText());

        // if the next token is not a semicolon, assume it is a command
        // containing the initial value.
        context->getToken(&tok);
        ASTNode *initVal = 0;
        if (tok.type != TOK_SEMI && tok.type != TOK_END) {
            context->putBack(&tok);
            initVal = parseExpression(context);
        }

        int index = --context->lastLocalVarOffset;

        Type *type = initVal ? initVal->type : context->types->word;

        // add a new definition for the variable to the local symbol table.
        context->add(varName, new LocalVarRefParser(type, index));

        return new AutoVarDefAST(type, index, initVal);
    }
};

// Interface for objects that need to know about when a function that doesn't
// yet have an address gets its address.
struct FuncWatcher {

    // gets called when the function we're watching gets an address.
    virtual void setAddr(CodeGen *cg, void *addr) = 0;
};

struct IncompleteFuncCallAST : public FuncCallAST, public FuncWatcher {
    void **offsetLoc;

    IncompleteFuncCallAST(Type *type, int argCount) :
        FuncCallAST(type, 0, argCount),
        offsetLoc(0) {
    }

    void emit(CodeGen *cg) {
        if (addr) {
            FuncCallAST::emit(cg);
        } else {
            emitArgs(cg);
            offsetLoc = cg->emitIncompleteCall();
            cg->emitSPAdd(argCount * sizeof(void *));
        }
    }

    void setAddr(CodeGen *cg, void *fixedAddr) {
        if (offsetLoc)
            cg->fixAddr(offsetLoc, addr);
        addr = fixedAddr;
    }
};

struct FuncDefAST : ASTNode {
    BlockAST *body;
    void *addr;
    Array<FuncWatcher *> watchers;
    int stackReservation;

    FuncDefAST(Type *type, BlockAST *body, int stackReservation) :
        ASTNode(type), 
        body(body), 
        addr(0), 
        watchers(4),
        stackReservation(stackReservation) {
    }

    ~FuncDefAST() { delete body; }
    void emit(CodeGen *cg) {
        void **incompleteAddr = cg->emitIncompleteJump();
        void *funcAddr = cg->curCode;
        cg->emitFuncEnter();
        cg->emitSPAdd(-stackReservation);
        body->emit(cg);
        cg->emitSPAdd(stackReservation);
        cg->emitReturn();
        cg->fixAddr(incompleteAddr, cg->curCode);

        // notify all of the watchers
        for (int i = 0; i < watchers.size; ++i)
            watchers[i]->setAddr(cg, funcAddr);

        // and store the address so IncompleteFuncCallParser can isolate
        // itself.
        addr = funcAddr;
    }

    void addWatcher(FuncWatcher *caller) {
        watchers.add(caller);
    }
};

struct IncompleteFuncCallParser : public FuncCallParser {
    FuncDefAST *funcDef;

    IncompleteFuncCallParser(FuncDefAST *funcDef, Type *returnType, int argCount) :
        FuncCallParser(0, returnType, argCount),
        funcDef(funcDef) {
    }

    ASTNode *parse(CompileContext *context) {
        IncompleteFuncCallAST *node =
            new IncompleteFuncCallAST(context->types->word, argCount);
        innerParse(context, node);
        funcDef->addWatcher(node);
        return node;
    }

    Parser *isolate() {
        assert(funcDef->addr);
        return new FuncCallParser(funcDef->addr, returnType, argCount);
    }
};

struct FuncDefParser : Parser {
    ASTNode *parse(CompileContext *context) {
        Token tok;
        context->getToken(&tok);
        if (tok.type != TOK_ID)
            context->error("Identifier expected, got: ", &tok);

        char *funcName = context->manage(tok.copyText());

        // create a new nested context
        CompileContext funcContext(context, true);

        // get the arg list, if any
        int argCount = 0;
        funcContext.getToken(&tok);
        if (tok.type == TOK_LPAREN) {
            while (tok.type != TOK_RPAREN) {
                funcContext.getToken(&tok);
                if (tok.type == TOK_ID)
                    funcContext.add(context->manage(tok.copyText()),
                                    new LocalVarRefParser(context->types->word,
                                                          argCount++ + 2
                                                          )
                                    );
                else if (tok.type != TOK_RPAREN)
                    funcContext.error("Identifier or closing paren expected, "
                                       "got: ",
                                      &tok
                                      );
            }

            funcContext.getToken(&tok);
        }

        // parse the function body
        if (tok.type != TOK_LCURLY) {
            tok.dump();
            funcContext.error("Expected Left Bracket, got: ", &tok);
        }

        BlockAST *body = parseBlock(&funcContext);

        funcContext.getToken(&tok);
        if (tok.type != TOK_RCURLY)
            funcContext.error("Expected right Bracket, got: ", &tok);

        FuncDefAST *funcDef = new FuncDefAST(context->types->word, body,  
                                             -funcContext.lastLocalVarOffset * 
                                              sizeof(void *)
                                             );

        // using word as the return type, should really extract it from the 
        // values returned.        
        context->add(funcName,
                     new IncompleteFuncCallParser(funcDef, 
                                                  context->types->word,
                                                  argCount
                                                  )
                     );
        return funcDef;
    }
};

struct IfElseAST : public ASTNode {
    ASTNode *condition, *ifBlock, *elseBlock;

    // stack reservations are positive - they are the number of bytes of stack 
    // space that need to be reserved for the context of the node.
    int stackReservation;
    IfElseAST(Type *type, ASTNode *condition, ASTNode *ifBlock,
              ASTNode *elseBlock,
              int stackReservation
              ) :
        ASTNode(type),
        condition(condition),
        ifBlock(ifBlock),
        elseBlock(elseBlock),
        stackReservation(stackReservation) {
    }

    ~IfElseAST() {
        delete condition;
        delete ifBlock;
        delete elseBlock;
    }

    void emit(CodeGen *cg) {
        // allocate all the space we'll need on the stack
        cg->emitSPAdd(-stackReservation);
        condition->emit(cg);
        void **elseJump = cg->emitIncompleteJZ();
        ifBlock->emit(cg);
        if (elseBlock) {
            void **endJump = cg->emitIncompleteJump();
            cg->fixAddr(elseJump, cg->curCode);
            elseBlock->emit(cg);
            cg->fixAddr(endJump, cg->curCode);
        } else {
            cg->fixAddr(elseJump, cg->curCode);
        }
        cg->emitSPAdd(stackReservation);
    }
};

struct IfElseParser : public Parser {
    ASTNode *parse(CompileContext *context) {
        Token tok;
        context->expect(TOK_LPAREN, "Expected left paren, got ");

        CompileContext ifCtx(context, false);

        ASTNode *condition = parseBlock(&ifCtx);
        ifCtx.expect(TOK_RPAREN, "Expected end parenthesis, got ");

        ifCtx.getToken(&tok);
        ASTNode *ifBlock, *elseBlock = 0;
        if (tok.type != TOK_LCURLY) {
            ifCtx.putBack(&tok);
            ifBlock = parseExpression(&ifCtx);
        } else {
            ifBlock = parseBlock(&ifCtx);

            ifCtx.expect(TOK_RCURLY,
                         "Expected right curly bracket after if block, got "
                         );

            // check for an "else"
            ifCtx.getToken(&tok);
            if (tok.type == TOK_ID &&
                !strncmp(tok.text, "else", tok.size)) {

                context->expect(TOK_LCURLY,
                                "Expected left curly bracket after 'else', got "
                                );
                elseBlock = parseBlock(&ifCtx);

                ifCtx.expect(TOK_RCURLY,
                             "Expected right curly bracket after 'else' "
                              "block, got "
                             );
            } else {
                // put this back into the outer context so we can parse it
                // outside of here.
                context->putBack(&tok);
            }
        }

        Type *type;
        if (ifBlock && elseBlock && ifBlock->type == elseBlock->type)
            type = ifBlock->type;
        else
            type = context->types->word;

        return new IfElseAST(type, condition, ifBlock, elseBlock, 
                             context->stackOffset(&ifCtx)
                             );
    }
};

struct VarAssignParser : public Parser {
    ASTNode *parse(CompileContext *context) {
        Token tok;
        context->getToken(&tok);
        if (tok.type != TOK_ID)
            context->error("Expected variable name after 'set', got ", &tok);

        Parser *var = context->lookUp(tok.text);
        if (!var)
            context->error("Undefined variable: ", &tok);
        VarRefParser *vrp = dynamic_cast<VarRefParser *>(var);
        if (!vrp)
            context->error("Not a variable reference: ", &tok);

        return vrp->createStoreAST(parseExpression(context));
    }
};

// holds information that needs to be communicated between a loop command and
// its nested break and continue commands - namely the start address of the
// loop and the array of incomplete addresses to fill in with the end address
// of the loop.
struct LoopInfo {
    void *beginningAddr;
    Array<void **> incompleteEndAddrs;
    
    LoopInfo(void *beginningAddr) :
        beginningAddr(beginningAddr),
        incompleteEndAddrs(4) {
    }
};

struct LoopAST : public ASTNode {
    // body has to get filled in after the fact because the node is passed
    // into the parsing of the body.
    ASTNode *body;

    // stack reservations are positive - they are the number of bytes of stack 
    // space that need to be reserved for the context of the node.
    int stackReservation;

    LoopAST(Type *type) : ASTNode(type), body(0), stackReservation(-1) {}
    ~LoopAST() {
        delete body;
    }
    void emit(CodeGen *cg) {
        assert(stackReservation != -1);
        cg->emitSPAdd(-stackReservation);
        void *addr = cg->curCode;
        LoopInfo info(cg->curCode);
        cg->setKey(this, &info);
        body->emit(cg);
        cg->deleteKey(this);

        // emit the stack cleanup and jump back to the beginning of the loop
        cg->emitJump(addr);

        // back-fill the "break" addresses
        for (int i = 0; i < info.incompleteEndAddrs.size; ++i)
            cg->fixAddr(info.incompleteEndAddrs[i], cg->curCode);

        // undo the stack reservation at the end of the loop.
        cg->emitSPAdd(stackReservation);
    }
};

struct BreakAST : public ASTNode {
    ASTNode *loop;
    int stackOffset;
    BreakAST(Type *type, ASTNode *loop, int stackOffset) : 
        ASTNode(type), 
        loop(loop),
        stackOffset(stackOffset) {
    }
    void emit(CodeGen *cg) {
        LoopInfo *info = (LoopInfo *)cg->getKey(loop);
        cg->emitSPAdd(stackOffset);
        info->incompleteEndAddrs.add(cg->emitIncompleteJump());
    }
};

struct ContinueAST : public ASTNode {
    ASTNode *loop;
    int stackOffset;
    ContinueAST(Type *type, ASTNode *loop, int stackOffset) : 
        ASTNode(type), 
        loop(loop), 
        stackOffset(stackOffset) {
    }
    void emit(CodeGen *cg) {
        LoopInfo *info = (LoopInfo *)cg->getKey(loop);
        cg->emitSPAdd(stackOffset);
        cg->emitJump(info->beginningAddr);
    }
};

// parse a break statement - break means "jump to the code after the loop"
struct BreakParser : public Parser {
    ASTNode *loopAST;
    CompileContext *loopContext;
    BreakParser(CompileContext *loopContext, ASTNode *loopAST) : 
        loopAST(loopAST),
        loopContext(loopContext) {
    }
    ASTNode *parse(CompileContext *context) {
        return new BreakAST(context->types->word, loopAST, 
                            loopContext->stackOffset(context)
                            );
    }

    Parser *isolate() {
        // should never be doing this
        assert(0);
    }
};

// parse a continue statement - continue means "jump to the beginning of the
// loop"
struct ContinueParser : public Parser {
    ASTNode *loopAST;
    CompileContext *loopContext;
    ContinueParser(CompileContext *loopContext, ASTNode *loopAST) : 
        loopAST(loopAST),
        loopContext(loopContext) {
    }
    ASTNode *parse(CompileContext *context) {
        return new ContinueAST(context->types->word, loopAST,
                               loopContext->stackOffset(context)
                               );
    }

    Parser *isolate() {
        // should never be doing this
        assert(0);
    }
};

// Parse a loop command:
// loop <block>
// loop <break-command-id> <block>
// loop <continue-command> <break-command> <block>
struct LoopParser : public Parser {
    ASTNode *parse(CompileContext *context) {
        Token tok;
        context->getToken(&tok);

        // store the current stack bottom so we can restore it later

        char *breakId = 0, *continueId = 0;
        if (tok.type == TOK_ID) {
            breakId = context->manage(tok.copyText());
            context->getToken(&tok);

            if (tok.type == TOK_ID) {
                continueId = breakId;
                breakId = context->manage(tok.copyText());
                context->getToken(&tok);
            }
        }

        if (tok.type != TOK_LCURLY)
            context->error("Expected left-curly bracket, got ", &tok);

        LoopAST *loopAST = new LoopAST(context->types->word);

        // create a loop context containing the break and continue commands
        CompileContext loopContext(context, false);
        if (breakId)
            loopContext.add(breakId, new BreakParser(&loopContext, loopAST));
        if (continueId)
            loopContext.add(continueId, new ContinueParser(&loopContext, loopAST));

        loopAST->body = parseBlock(&loopContext);
        loopAST->stackReservation = (context->lastLocalVarOffset - 
                                     loopContext.lastLocalVarOffset) * 
                                     sizeof(void *);
        loopContext.expect(TOK_RCURLY,
                           "Right curly brace expected after loop block, got "
                           );

        return loopAST;
    }
};

// parse a module command
// module <name> <block>
struct ModuleParser : public Parser {
    ASTNode *parse(CompileContext *context) {
        Token modName;
        context->getToken(&modName);
        if (modName.type != TOK_STR)
            context->error("Expected string module name, got ", &modName);

        if (context->root->lookUpModule(modName.text))
            context->error("Module already defined: ", &modName);

        Token tok;
        context->getToken(&tok);
        if (tok.type != TOK_LCURLY)
            context->error("Expected left-curly bracket, got ", &tok);

        CompileContext moduleContext(context->root, true);
        moduleContext.toker = context->toker;
        ASTNode *ast = moduleContext.parseAndRun();
        moduleContext.isolate();
        delete ast;
        context->root->storeModule(modName.text, new SymTab(moduleContext.symtab));

        moduleContext.getToken(&tok);
        if (tok.type != TOK_RCURLY)
            context->error("Expected right-curly bracket, got ", &tok);

        return new NopAST(context->types->word);
    }
};

struct BinOpAST : public ASTNode {
    ASTNode *a, *b;
    ~BinOpAST() {
        delete a;
        delete b;
    }

    BinOpAST(Type *type, ASTNode *a, ASTNode *b) : ASTNode(type), a(a), b(b) {}
};

struct EqCheckAST : public BinOpAST {
    EqCheckAST(Type *type, ASTNode *a, ASTNode *b) : BinOpAST(type, a, b) {}
    void emit(CodeGen *cg) {
        b->emit(cg);
        cg->emitStoreArg(1, HINT_EQ);
        a->emit(cg);
        cg->emitEqTest();
    }
};

struct EqCheckParser : public Parser {
    ASTNode *parse(CompileContext *context) {
        ASTNode *primary = context->primary;
        assert(primary);
        return new EqCheckAST(context->types->word, primary,
                              parseExpression(context)
                              );
    }
};

struct NEqCheckAST : public BinOpAST {
    NEqCheckAST(Type *type, ASTNode *a, ASTNode *b) : BinOpAST(type, a, b) {}
    void emit(CodeGen *cg) {
        b->emit(cg);
        cg->emitStoreArg(1, HINT_EQ);
        a->emit(cg);
        cg->emitNEqTest();
    }
};

struct NEqCheckParser : public Parser {
    ASTNode *parse(CompileContext *context) {
        ASTNode *primary = context->primary;
        assert(primary);
        return new NEqCheckAST(context->types->word, primary,
                               parseExpression(context)
                               );
    }
};

struct SPAST : public ASTNode {
    SPAST(Type *type) : ASTNode(type) {}
    void emit(CodeGen *cg) {
        cg->emitLoadSP();
    }
};

struct SPParser : public Parser {
    ASTNode *parse(CompileContext *context) {
        return new SPAST(context->types->word);
    }
};

struct AddAST : public BinOpAST {
    AddAST(Type *type, ASTNode *a, ASTNode *b) : BinOpAST(type, a, b) {}
    void emit(CodeGen *cg) {
        b->emit(cg);
        cg->emitStoreArg(1, HINT_ADD);
        a->emit(cg);
        cg->emitAdd();
    }
};

struct AddParser : public Parser {
    ASTNode *parse(CompileContext *context) {
        ASTNode *primary = context->primary;
        assert(primary);
        return new AddAST(primary->type, primary, parseExpression(context));
    }
};

struct SubAST : public BinOpAST {
    SubAST(Type *type, ASTNode *a, ASTNode *b) : BinOpAST(type, a, b) {}
    void emit(CodeGen *cg) {
        b->emit(cg);
        cg->emitStoreArg(1, HINT_SUB);
        a->emit(cg);
        cg->emitSub();
    }
};

struct SubParser : public Parser {
    ASTNode *parse(CompileContext *context) {
        ASTNode *primary = context->primary;
        assert(primary);
        return new SubAST(primary->type, primary, parseExpression(context));
    }
};

struct MulAST : public BinOpAST {
    MulAST(Type *type, ASTNode *a, ASTNode *b) : BinOpAST(type, a, b) {}
    void emit(CodeGen *cg) {
        b->emit(cg);
        cg->emitStoreArg(1, HINT_SUB);
        a->emit(cg);
        cg->emitMul();
    }
};

struct MulParser : public Parser {
    ASTNode *parse(CompileContext *context) {
        ASTNode *primary = context->primary;
        assert(primary);
        return new MulAST(context->types->word, primary,
                          parseExpression(context)
                          );
    }
};

struct DivAST : public BinOpAST {
    DivAST(Type *type, ASTNode *a, ASTNode *b) : BinOpAST(type, a, b) {}
    void emit(CodeGen *cg) {
        b->emit(cg);
        cg->emitStoreArg(1, HINT_DIV);
        a->emit(cg);
        cg->emitDiv();
    }
};

struct DivParser : public Parser {
    ASTNode *parse(CompileContext *context) {
        ASTNode *primary = context->primary;
        assert(primary);
        return new DivAST(context->types->word, primary,
                          parseExpression(context)
                          );
    }
};

struct NotAST : public ASTNode {
    ASTNode *op;
    NotAST(Type *type, ASTNode *op) : ASTNode(type), op(op) {}
    ~NotAST() { delete op; }
    void emit(CodeGen *cg) {
        op->emit(cg);
        cg->emitNot();
    }
};

struct NotParser : public Parser {
    ASTNode *parse(CompileContext *context) {
        return new NotAST(context->types->word, parseExpression(context));
    }
};

void puti(void *val) {
    printf("%p", val);
}

struct StandardPostParser : public Parser {
    SymTab symtab;

    ASTNode *parse(CompileContext *context) {
        Token tok;
        context->getToken(&tok);
        if (tok.type == TOK_DOT) {
            context->getToken(&tok);
            if (tok.type != TOK_ID)
                context->error("Expected identifier, got ", &tok);

            Parser *parser = symtab.lookUp(tok.text);
            if (!parser)
                context->error("Not a known selector for this type: ", &tok);

            return parser->parse(context);
        } else if (tok.type == TOK_ID) {
            Parser *parser = symtab.lookUp(tok.text);
            if (parser) {
                return parser->parse(context);
            } else {
                context->putBack(&tok);
                return context->primary;
            }
        } else {
            context->putBack(&tok);
            return context->primary;
        }
    }
};

Parser *createIntPostParser(CompileContext *context) {
    StandardPostParser *p = new StandardPostParser;
    p->symtab.add("+", context->manage(new AddParser));
    p->symtab.add("-", context->manage(new SubParser));
    p->symtab.add("*", context->manage(new MulParser));
    p->symtab.add("/", context->manage(new DivParser));
    p->symtab.add("==", context->manage(new EqCheckParser));
    p->symtab.add("!=", context->manage(new NEqCheckParser));
    return p;
}

// import 'filename' symbola symbolb(alias) symbolc;
// import compiler symbola symbolb;
struct ImportParser : public Parser {

    void importSymbols(CompileContext *context, SymTab *symtab) {
        Token tok;
        context->getToken(&tok);
        while (!tok.isTerminator()) {
            if (tok.type == TOK_ID) {
                Entry *entry = symtab->findEntry(tok.text);
                if (!entry)
                    context->error("Symbol is not in module: ", &tok);
                const char *name = entry->name;
                Parser *p = entry->parser;

                // see if we got a "(alias)"
                context->getToken(&tok);
                if (tok.type == TOK_LPAREN) {
                    context->getToken(&tok);
                    if (tok.type != TOK_ID)
                        context->error("Expected identifier for alias, got ",
                                    &tok);

                    name = context->manage(tok.copyText());
                    context->expect(TOK_RPAREN,
                                    "Expected right paren after alias, got ");
                    context->getToken(&tok);
                }

                // store it in the new symbol table
                context->symtab.add(name, p);
            } else {
                context->error("Expected identifier, got ", &tok);
            }

        }
    }

    ASTNode *parse(CompileContext *context) {
        Token tok;
        SymTab *ns;
        context->getToken(&tok);
// need to define "import compiler" usage
//        if (tok.type == TOK_ID) {
//            if (strcmp(tok.text, "compiler"))
//                context->error("Invalid identifier: ", &tok);
//            importSymbols(context, &compileNS);
//        } else
        if (tok.type == TOK_STR) {
            // see if the module has been cached
            if (SymTab *module =
                 context->root->lookUpModule(tok.text)) {

                importSymbols(context, module);
            } else {
                CompileContext moduleContext(context->root, true);
                FILE *fp = fopen(tok.text, "r");
                if (!fp)
                    context->error("Unable to load module: ", &tok);
                ASTNode *ast = moduleContext.loadModule(tok.text, fp);
                moduleContext.isolate();
                delete ast;
                importSymbols(context, &moduleContext.symtab);
                fclose(fp);
                context->root->storeModule(tok.text,
                                           new SymTab(moduleContext.symtab)
                                           );
            }
        } else {
            context->error("Expected filename or special module name after "
                           "import, got ", &tok);
        }

        return new NopAST(context->types->word);
    }
};

// Parser for a command that is a type identifier.
struct TypeParser : public Parser {

    // this is essentially the "meta-type" - a type with a post parser for
    // type subcommands.
    Type *type;

    TypeParser(Type *type) : type(type) {}

    ASTNode *parse(CompileContext *context) {
        return new NopAST(type);
    }
};

// "stack vars" are non-word objects that are allocated on the stack.

struct StackVarRefAST : public ASTNode {
    int index;

    StackVarRefAST(Type *type, int index) : ASTNode(type), index(index) {}

    void emit(CodeGen *cg) {
        cg->emitLoadLocalVarAddr(index);
    }
};

struct StackVarRefParser : public Parser {
    Type *type;
    int index;
    size_t size;

    StackVarRefParser(Type *type, int index, size_t size) :
        type(type),
        index(index),
        size(size) {
    }

    ASTNode *parse(CompileContext *context) {
        return new StackVarRefAST(type, index);
    }
};

struct StackVarDefAST : public ASTNode {
    size_t size;
    int offset;
    StackVarDefAST(Type *type, size_t size, int offset) : 
        ASTNode(type), 
        size(size),
        offset(offset) {
    }
    void emit(CodeGen *cg) {
        // point accumulator at the variable
        cg->emitLoadLocalVarAddr(offset);
    }
};

// Type.svar identifier
struct StackVarDefParser : public Parser {
    Type *type;
    size_t size;
    Parser *constructorParser;
    StackVarDefParser(Type *type, size_t size, Parser *constructorParser) :
        type(type),
        size(size),
        constructorParser(constructorParser) {
    }

    ASTNode *parse(CompileContext *context) {

        // delete the primary, it should just be a NopAST referencing our
        // meta-type and we no longer need it.
        delete context->primary;

        Token tok;
        context->getToken(&tok);
        if (tok.type != TOK_ID)
            context->error("Identifier expected, got ", &tok);

        // calculate the number of words required to store the type.  Store
        // the new offset as the index.
        int words = (size - 1) / sizeof(void *) + 1;
        int index = context->lastLocalVarOffset -= words;
        context->add(context->manage(tok.copyText()),
                     new StackVarRefParser(type, index, size)
                     );

        // pass it on to the constructor
        context->primary = new StackVarDefAST(type, size, index);
        return constructorParser->parse(context);
    }
};

struct FieldRefAST : public ASTNode {
    ASTNode *target;
    size_t offset;
    FieldRefAST(Type *type, ASTNode *target, size_t offset) :
        ASTNode(type),
        target(target),
        offset(offset) {
    }

    ~FieldRefAST() {
        delete target;
    }

    void emit(CodeGen *cg) {
        cg->loadAddr((void *)offset);
        cg->emitStoreArg(1, HINT_ADD);
        target->emit(cg);
        cg->emitAdd();
        cg->emitLoad();
    }
};

struct FieldRefParser : public Parser {
    Type *type;
    size_t offset;
    FieldRefParser(Type *type, size_t offset) : type(type), offset(offset) {}

    ASTNode *parse(CompileContext *context) {
        ASTNode *target = context->releasePrimary();
        return new FieldRefAST(type, target, offset);
    }
};

// Promote converts a variable to the specified type.
struct PromoteParser : public Parser {
    Type *type;
    PromoteParser(Type *type) : type(type) {}
    ASTNode *parse(CompileContext *context) {
        // parse an identifier
        Token tok;
        context->getToken(&tok);
        if (tok.type != TOK_ID)
            context->error("Expected identifier, got ", &tok);

        // look up the variable reference for the token
        Parser *parser = context->lookUp(tok.text);
        if (!parser)
            context->error("Undefined variable ", &tok);

        LocalVarRefParser *varRef = dynamic_cast<LocalVarRefParser *>(parser);
        if (!varRef)
            context->error("Symbol is not a variable: ", &tok);
        varRef->type = type;
        return varRef->parse(context);
    }
};

void *operator new(size_t size, void *mem) { return mem; }

void *Token_init(void *mem) {
    return new(mem) Token();
}

void *CompileContext_init(void *mem, CompileContext *parent) {
    return new(mem) CompileContext(parent, true);
}

void CompileContext_getToken(CompileContext *context, Token *tok) {
    context->getToken(tok);
}

void CompileContext_add(CompileContext *context, const char *name,
                        Parser *parser) {
    context->symtab.add(name, parser);
}

void CompileContext_eval(CompileContext *context, char *code) {
    context->eval(code);
}

// UserParser lets us extend a parser in MINAPL.

struct UserParser;

struct UserParserVTable {

    // we don't care as much about the type of the "init" method as we don't
    // call it from C++ anyway.
    void *init;

    typedef void (*Destructor)(UserParser *parser);
    Destructor destructor;

    typedef ASTNode *(*ParseMethod)(UserParser *parser, CompileContext *context);
    ParseMethod parse;
};

struct UserParser : public Parser {
    UserParserVTable *vtable;
    void *data;

    UserParser(UserParserVTable *vtable, void *data) :
        vtable(vtable),
        data(data) {
    }

    ~UserParser() {
        vtable->destructor(this);
    }

    ASTNode *parse(CompileContext *context) {
        return vtable->parse(this, context);
    }
};

#if 0
struct UserParserNewAST : public ASTNode {
    void emit(CodeGen *cg) {
        // emit code to allocate a new UserParser and init it and store it in
        // the accumulator.

        // allocate a new instance
        cg->loadAddr(reinterpret_cast<void *>(sizeof(UserParser)));
        cg->emitStoreArg(0, HINT_CALL);
        cg->emitFuncCall(reinterpret_cast<void *>(malloc));

        // store the pointer on the stack
        cg->pushLast();
        int parserVar = cg->stackBottom -= sizeof(void *);

        // do C++ constructor
        cg->emitStoreArg(0, HINT_CALL);
        cg->emitFuncCall(UserParser_init);

        // do minapl init function
        cg->emitLocalVarRef(parserVar);
        cg->emitStoreArg(0, HINT_CALL);
    }
};
#endif

struct UserParserDefAST : public ASTNode {
    FuncDefAST *initFunc, *delFunc, *parseFunc, *newFunc;

    UserParserDefAST(Type *type) : ASTNode(type) {}
    
    ~UserParserDefAST() {
        delete initFunc;
        delete delFunc;
        delete parseFunc;
        delete newFunc;
    }

    void emit(CodeGen *cg) {
        // if this is the first time this is called for this ast node, then we
        // want to emit the functions and emit the vtable structure into the
        // global heap.
        UserParserVTable *vtab;
        if (!(vtab = reinterpret_cast<UserParserVTable *>(cg->getKey(this)))) {
            initFunc->emit(cg);
            delFunc->emit(cg);
            parseFunc->emit(cg);
            newFunc->emit(cg);

            // allocate space in the global pool, add the function definitions
            // to it.
            void *mem = cg->allocGlobal(sizeof(UserParserVTable));
            vtab = new (mem) UserParserVTable();
            vtab->init = initFunc->addr;
            vtab->destructor =
                reinterpret_cast<UserParserVTable::Destructor>(delFunc->addr);
            vtab->parse =
                reinterpret_cast<UserParserVTable::ParseMethod>(parseFunc->addr);
        }
    }
};

// macro to create an error message on the stack from a template and a string
// argument.
#define ERROR_MSG(var, template, data) \
    char *var; \
    { static char tmplt_[] = template; \
      var = (char *)alloca(sizeof(template) + strlen(data)); \
    sprintf(var, tmplt_, data); } \


// Make sure that context's symtab contains a func with the given name and
// number of args and returns it.
// If numArgs is negative, check for _at least_ that many arguments but allow
// more.
IncompleteFuncCallParser *checkFunc(CompileContext *context,
                                    const char *name,
                                    int numArgs
                                    ) {
    Parser *parser = context->symtab.lookUp(name);
    IncompleteFuncCallParser *fcp =
        dynamic_cast<IncompleteFuncCallParser *>(parser);
    if (!fcp) {
        ERROR_MSG(msg, "No function %s", name);
        context->error(msg);
    }
    if (numArgs < 0 && fcp->argCount < -numArgs ||
        numArgs >= 0 && fcp->argCount != numArgs) {
        ERROR_MSG(msg, "Wrong number of args for function %s", name);
        context->error(msg);
    }
    return fcp;
}

/**
 * When the address for a function becomes available, stores it in a function
 * pointer and deletes itself.
 */
struct VTableFixer : public FuncWatcher {

    void **funcPointerAddr;

    VTableFixer(void **funcPointerAddr) : funcPointerAddr(funcPointerAddr) {}

    void setAddr(CodeGen *cg, void *addr) {
        *funcPointerAddr = addr;
        delete this;
    }
};

void uninitializedVTableFunc() {
    fprintf(stderr, "Uninitialized VTable function called.");
    abort();
}

/**
 * Create a "new" function that creates an instances of something and
 * initializes it.
 * @param initFunc the initializer function that gets called after allocation.
 * @param size size of the object to allocate.
 * @param argCount number of args of the init func.  The "new" func will have 
 *     one less arg.
 */
IncompleteFuncCallParser *createNewFunc(CompileContext *context, Type *resultType, 
                                        FuncDefAST *initFunc, 
                                        size_t size,
                                        int argCount
                                        ) {
    // we need an ast node to generate the new function
    BlockAST *block = new BlockAST(resultType);

    // var inst malloc sizeof-instance
    FuncCallAST *mallocCall = new FuncCallAST(resultType, (void *)malloc, 1);
    mallocCall->args[0] = new ConstAST(context->types->word, (void *)size);
    ASTNode *mallocResult = new AutoVarDefAST(resultType, -1, mallocCall);
    block->add(mallocResult);

    // inst.init args
    IncompleteFuncCallAST *initCall = new IncompleteFuncCallAST(context->types->word,
                                                                argCount
                                                                );
    initFunc->addWatcher(initCall);

    // add the arguments, set the zeroth argument to the newly allocated 
    // object.
    // the "new" function is going to have all of the same arguments as the 
    // init function except for the 0th arg.  The 0th arg of the "new" 
    // function is going to be the class object, which we ignore.
    for (int i = argCount - 1; i > 0 ; --i)
        initCall->args[i] = new LocalVarRefAST(context->types->word, i + 2);
    initCall->args[0] = new LocalVarRefAST(resultType, -1);
    block->add(initCall);

    // return inst
    block->add(new LocalVarRefAST(resultType, -1));

    FuncDefAST *funcDefAST = new FuncDefAST(context->types->word, block, 
                                            sizeof(void *)
                                            );
    return new IncompleteFuncCallParser(funcDefAST, resultType, argCount);
}

// This command defines a user parser.
struct UserParserDefParser : public Parser {
    ASTNode *parse(CompileContext *context) {
        // get the name
        Token tok;
        context->getToken(&tok);
        if (tok.type != TOK_ID)
            context->error("Identifier expected for UserParser type name, "
                           "got ", &tok);
        char *name = context->manage(tok.copyText());

        // expect an opening bracket
        context->expect(TOK_LCURLY,
                        "Expected opening bracket in UserParser definition, "
                        "got ");

        CompileContext subcontext(context, false);
        ASTNode *blockAST = parseBlock(&subcontext);
        UserParserDefAST *userDefAST =
            new UserParserDefAST(context->types->word);

        subcontext.expect(TOK_RCURLY, "Expected a closing brace, got ");

        // We need three functions - 'init(this)', 'del(this)' and
        // 'parse(this context)'
    
        IncompleteFuncCallParser *initParser = 
            checkFunc(&subcontext, "init", -1);
        userDefAST->initFunc = initParser->funcDef;
        userDefAST->delFunc = checkFunc(&subcontext, "del", 1)->funcDef;
        userDefAST->parseFunc = checkFunc(&subcontext, "parse", 2)->funcDef;

        // register a new type for it in the current context
        UserParserVTable *vtable = new UserParserVTable();

        // initialize the VTable with a sentinel
        vtable->init = (void *)uninitializedVTableFunc;
        vtable->destructor = (UserParserVTable::Destructor)uninitializedVTableFunc;
        vtable->parse = (UserParserVTable::ParseMethod)uninitializedVTableFunc;

        // subscribe the vtable to the instantiation of the functions
        userDefAST->initFunc->addWatcher(new VTableFixer(&vtable->init));
        userDefAST->delFunc->addWatcher(new VTableFixer((void **)&vtable->destructor));
        userDefAST->parseFunc->addWatcher(new VTableFixer((void **)&vtable->parse));

        StandardPostParser *classParser = new StandardPostParser;
        Type *classType = new Type(classParser);

        IncompleteFuncCallParser *newCallParser = createNewFunc(context, classType,
                                                                userDefAST->initFunc,
                                                                sizeof(UserParser),
                                                                initParser->argCount
                                                                );
        context->manage(newCallParser);
        userDefAST->newFunc = newCallParser->funcDef;

        classParser->symtab.add("new", newCallParser);
        context->types->add(classType);
        context->symtab.add(name, 
                            context->manage(new ConstParser(classType, vtable))
                            );

        return userDefAST;
    }
};

struct TypeAndMeta {
    Type *type, *metaType;
    SymTab *symtab;
};

Type *createType(TypeAndMeta *info, CompileContext *rc, SymTab *mod,
                 const char *name,
                 size_t size,
                 Parser *constructor
                 ) {
    StandardPostParser *typeParser = new StandardPostParser;
    StandardPostParser *metaTypeParser = new StandardPostParser;

    // create the type and the meta type
    Type *metaType = new Type(metaTypeParser), *type = new Type(typeParser);
    rc->types->add(metaType);
    rc->types->add(type);

    // create the meta type's subcommands

    // "svar" if the type can be constructed
    if (size && constructor) {
        rc->manage(constructor);
        metaTypeParser->symtab.add("svar",
                                   rc->manage( new StackVarDefParser(
                                                type,
                                                size,
                                                constructor
                                               )
                                              )
                                   );
    }

    // the "promote" command
    metaTypeParser->symtab.add("promote",
                               rc->manage(new PromoteParser(type))
                               );

    info->type = type;
    info->metaType = metaType;
    info->symtab = &typeParser->symtab;
    mod->add(name, rc->manage(new TypeParser(metaType)));

    return type;
}

SymTab *createCompilerModule(CompileContext *rc) {
    SymTab *mod = new SymTab();


    // Token type - create the meta-type and the token type
    TypeAndMeta token;
    FuncCallParser *cons;
    Type *tokType = createType(&token, rc, mod, "Token", sizeof(Token),
                               cons = new FuncCallParser((void *)Token_init, 0, 1)
                               );
    cons->returnType = tokType;

    // Add the field variables for the token type.
    token.symtab->add("type",
                      rc->manage( new FieldRefParser(
                                      rc->types->word,
                                      offsetof(Token, type)
                                  )
                                )
                      );

    // CompileContext
    TypeAndMeta cc;
    Type *ccType = createType(&cc, rc, mod, "CompileContext",
                              sizeof(CompileContext),
                              cons = new FuncCallParser((void *)CompileContext_init, 
                                                        0, 
                                                        2
                                                        )
                              );
    cons->returnType = ccType;
    cc.symtab->add("getToken",
                   rc->manage(new FuncCallParser((void *)CompileContext_getToken,
                                                 rc->types->word,
                                                 2
                                                 )
                              )
                   );
    cc.symtab->add("add",
                   rc->manage(new FuncCallParser((void *)CompileContext_add,
                                                 rc->types->word,
                                                 3
                                                 )
                              )
                   );
    cc.symtab->add("eval",
                   rc->manage(new FuncCallParser((void *)CompileContext_eval,
                                                 rc->types->word, // ASTNode
                                                 2
                                                 )
                              )
                   );

    // Parser
    mod->add("Parser", rc->manage(new UserParserDefParser()));

    // the root context
    mod->add("rootContext", 
             rc->manage(new ConstParser(ccType, rc->getRoot()))
             );

    return mod;
}

int main(int argc, const char **argv) {
    void (*func)();

    Types types;
    types.str = new Type(0);

    // emit the main function
    CompileContext rootContext(&types);
    types.word = new Type(createIntPostParser(&rootContext));
    rootContext.add("puts", new FuncCallParser((void *)puts, types.word, 1));
    rootContext.add("puti", new FuncCallParser((void *)puti, types.word, 1));
    rootContext.add("var", new VarDefParser);
    rootContext.add("defun", new FuncDefParser);
    rootContext.add("if", new IfElseParser);
    rootContext.add("set", new VarAssignParser);
    rootContext.add("loop", new LoopParser);
    rootContext.add("module", new ModuleParser);
    rootContext.add("sp", new SPParser);
    rootContext.add("import", new ImportParser);
    rootContext.add("not", new NotParser);

    rootContext.modules.add("//compiler", createCompilerModule(&rootContext));

    // create a nested context for the script
    CompileContext scriptContext(&rootContext, true);
    ASTNode *scriptAST;
    try {
        if (argc > 1) {
            FILE *fp = fopen(argv[1], "r");
            scriptAST = scriptContext.loadModule(argv[1], fp);
            fclose(fp);
        } else {
            scriptAST = scriptContext.loadModule("<stdin>", stdin);
        }
    } catch (const ParseError &ex) {
        fprintf(stderr, "%s\n", ex.message);
        exit(1);
    }

    delete scriptAST;
}
